/*jslint vars: true, forin: true */
/*global angular: false, engine: false, $: false*/
engine.factory("Step", function (Entity, config) {
	'use strict';
	var events = {
		change: (function() {
			var event = document.createEvent('HTMLEvents');
			event.initEvent("change", true, true);
			return event;
		}())
	};


	/**
	 * The Step class does not directly used in Process.
	 * Process use sub classes of Step. They are DelayStep,
	 * EventStep and Stepby. Those Classess extend from
	 * Step by manual method.
	 * Step is a Class which use to define operational action.
	 *
	 * @class Step
	 * @constructor
	 * @param object {string} CSSPath string of the object(HTMLElement).
	 * @param action {string} The event to trigger.
	 * @param [param=null] {string} The param of the event
	 * @param [data_key=""] {string} The column name by custom in the saving result.
	 */
	var Step = function Step(options) {
		options = options || {};
		// Construct a Step Object by three points.They are
		// "object", "action" and "action param".
		/**
		 * The object to be oprated.
		 * @property object
		 * @type string
		 */
		this.object = options.object || window[0];
		/**
		 * The action is the event of object need to trigger.
		 * @property action
		 * @type string
		 */
		this.action = options.action;
		/**
		 * The parameter of action. Most of the time do not need parameter.
		 * @property param
		 * @type string|null
		 * @default null
		 */
		this.param = options.param || null;
		/**
		 * The host of Step.
		 *
		 * @property $process
		 * @type Process
		 * @private
		 * @default null
		 */
		this.$process = null;

		/**
		 * Each Step cache the time of run in the current.
		 * Process loop by Unix timestamp(ms).
		 * @property run_timestamp
		 * @type number
		 */
		this.run_timestamp = 0;
		Entity.call(this, options);
	};
	Step.prototype = new Entity();
	Step.prototype.constructor = Step;

	/**
	 * Step core runing function.
	 * 不允许maxLoop超过字典长度
	 * @method $run
	 * @returns {boolean}
	 * @private
	 */
	Step.prototype.$run = function ($debug) {
		var param, object = $(this.object, window[0].document),
			action = this.action,
			loop = this.$process.loop,
			counter = this.$process.counter;

		// ==========Ready==========
		if (object.length !== 1 && object !== window[0]) {
			this.$process.pause();
			//TODO re-start with BackEnd.
			console.log("if object is null, pause the process!");
			this.$process.doException();
			return false;
		}

		// ==========Start==========
		if (true /*config.get("SHOW_AREA_MODE")*/ && action !== "scroll") {
			this.showArea(object);
		}

		// DOM functions, click(), other events.
		switch (action) {
		case "click":
			object.get(0).click();
			break;
		case "val":
			if (this.param.type === "direct") {
				param = this.param.value;
			} else if (this.param.type === "index") {
				param = this.$process.data[this.param.value];
			} else {
				//TODO 应该参数只有直接量和索引两种，其他的都是错的
			}

			object[action](param);
			object.get(0).dispatchEvent(events.change);
			break;
		case "scroll":
			this.object.scrollTo(this.param.position_left, this.param.position_top);
			break;
		default:
			object[action]();
			break;
		}

		// ==========Write==========
		if($debug !== true) {
			this.$process.log.push({
				action: action,
				param: param
			});
		}

		return true;
	};
	/**
	 * Step default runing function.
	 * @method run
	 */
	Step.prototype.run = function () {
		return this.$run();
	};
	/**
	 * Highlight the DOM of operation.
	 * @method showArea
	 * @param _object {HTMLElement}
	 * @returns {Step|DelayStep|EventStep|StepbyStep} this
	 * @chainable
	 */
	Step.prototype.showArea = function (object) {
		var now = (new Date()).getTime(),
			scroll_top = $(window[0].document).scrollTop(),
			scroll_left = $(window[0].document).scrollLeft();

		$('body').append('<div class="highlight" style="' +
			'top:' + (object.offset().top - scroll_top) +
			'px;left:' + (object.offset().left - scroll_left) +
			'px;width:' + object.get(0).offsetWidth +
			'px;height:' + object.get(0).offsetHeight +
			'px;" id="' + now + '"></div>');

		setTimeout(
			function () {
				$("#" + now).remove();
			},
			2000 /*config.get("HIGHLIGHT_REMOVE_DELAY")*/
		);

		return this;
	};
	/**
	 * Return it belongs to which process.(*process)
	 * @method getParent
	 * @returns {Process} The process this step in.
	 */
	Step.prototype.getParent = function () {
		return this.$process;
	};
	/**
	 * Set it belongs to which process.
	 * @method setParent
	 * @returns {Step|DelayStep|EventStep|StepbyStep} this
	 * @chainable
	 */
	Step.prototype.setParent = function (process) {
		this.$process = process;
		return this;
	};
	Step.prototype.interept = function () {
		//这是一个抽象方法
	};
	Step.prototype.toJson = function () {
		var json = {};
		json.name = this.getInfo("name");
		json.object = this.object;
		json.action = this.action;
		json.actionparam = angular.toJson(this.param);
		json.comment = this.getInfo("comment");

		var stepid = this.getInfo("ID");
		if (stepid !== "") {
			json.stepid = stepid;
		}
		return json;
	};
	Step.prototype.toOptions = function () {
		var options = {};
		angular.forEach(Entity.prototype.toOptions.call(this), function (value, key) {
			this[key] = value;
		}, options);
		options.object = this.object;
		options.param = this.param;
		options.action = this.action;

		return options;
	}

	return Step;
});
